<?php
namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use App\Jobs\PushMsgJob;
use App\Jobs\SendSystemMsgJob;
use App\Libraries\Easemob\Easemob;
use App\Libraries\Tools\ConstVar;
use App\Libraries\Tools\Msg;
use App\Models\HongBao\HongBaoTemplate;
use App\Models\MLM\MLMBalance;
use App\Models\MLM\MLMBalanceLog;
use App\Models\Msg\MsgSystem;
use App\Models\Post\Post;
use App\Models\Post\PostComment;
use App\Models\Post\PostFav;
use App\Models\Post\PostPraise;
use App\Models\Post\PostSta;
use App\Models\Shop\Shop;
use App\Models\Shop\ShopComment;
use App\Models\Shop\ShopCoupon;
use App\Models\Shop\ShopCouponConfig;
use App\Models\Shop\ShopCouponStatistic;
use App\Models\Shop\ShopStatistic;
use App\Models\Shop\ShopWithdrawApply;
use App\Models\Tool\CityStatistics;
use App\Models\Tool\District;
use App\Models\Tool\FailCron;
use App\Models\User\Balance;
use App\Models\User\Follow;
use App\Models\User\User;
use App\Models\User\UserPoint;
use App\Models\User\UserPointLog;
use App\Models\User\UserWithdrawApply;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;


class CronController extends Controller{


    protected $failCron;

    public function __construct(FailCron $failCron){
        $this->failCron = $failCron;
    }

    /**
     * shop statistic
     * @param ShopStatistic $sta
     * @return \Illuminate\Http\JsonResponse
     */
    public function shopSta(ShopStatistic $sta){
        $shop_id = [0];
        try{
            $data = DB::select("SELECT sc.shop_id, COUNT(1) AS `comment`, AVG(score) AS score
            FROM shop_comments sc
            WHERE sc.`status`=1
            GROUP BY sc.shop_id");
            foreach($data as $v){
                $shop_id[] = $v->shop_id;
                $sta->updateOrCreate(['shop_id'=>$v->shop_id], ['score'=>$v->score,'comment'=>$v->comment]);
            }
            $sta->whereNotIn('shop_id', $shop_id)->update(['score'=>5,'comment'=>0]);
        }catch (\Exception $e){
            $this->failCron->title = 'shop sta';
            $this->failCron->error_msg = $e->getMessage();
            $this->failCron->comment = 'shop sta fail';
            $this->failCron->save();
        }

        return $this->sendSuccessResponse([]);
    }

    public function couponSta(ShopCouponStatistic $shopCouponStatistic,
                              ShopStatistic $sta, ShopCouponConfig $config, ShopCoupon $shopCoupon){
        try{
            $config = $config->all();
            foreach($config as $v){
                $coupon_sta = $shopCouponStatistic->where('coupon_id', $v->coupon_id)->first();
                if(empty($coupon_sta)){
                    $shopCouponStatistic->coupon_id = $v->coupon_id;
                    $shopCouponStatistic->shop_id = $shopCoupon->where('id', $v->coupon_id)->first()->shop_id??0;
                    $shopCouponStatistic->sales = $v->base + rand($v->daily_min, $v->daily_max);
                    $shopCouponStatistic->save();
                    $shopCouponStatistic = new ShopCouponStatistic();
                }
                if($coupon_sta->sales < $v->base){
                    $coupon_sta->sales = $v->base + rand($v->daily_min, $v->daily_max);
                }else{
                    if($coupon_sta->sales <= $v->max || empty($v->max)){
                        $coupon_sta->sales = $coupon_sta->sales + rand($v->daily_min, $v->daily_max);
                    }
                }
                $coupon_sta->save();
            }
            $data = DB::select("SELECT shop_id, SUM(sales) AS sales
            FROM shop_coupon_statistics
            GROUP BY shop_id");
            foreach($data as $v){
                $sta->updateOrCreate(['shop_id'=>$v->shop_id], ['sales'=>$v->sales]);
            }
        }catch (\Exception $e){
            $this->failCron->title = 'coupon sta';
            $this->failCron->error_msg = $e->getMessage();
            $this->failCron->comment = 'coupon sta fail';
            $this->failCron->save();
        }
        return $this->sendSuccessResponse([]);
    }

    /**
     * post statistic
     * @param PostComment $comment
     * @param PostSta $sta
     * @return \Illuminate\Http\JsonResponse
     */
    public function postSta(PostComment $comment, PostSta $sta){

        $repost_id = [0];
        $comment_post_id = [0];
        $praise_post_id = [0];

        try{
            //冻结那些主评论被冻结的子评论
            $disabled_comment_ids = $comment->select('id')->where('comment_id', 0)->where('status', 0)->get()->toArray();
            $comment->whereIn('comment_id', $disabled_comment_ids)->update(['status'=>0]);

            $repost = DB::select("SELECT p.repost_id, COUNT(1) AS num
                FROM posts p
                WHERE p.`status`=1 AND p.repost_id<>0
                GROUP BY p.repost_id");
            foreach($repost as $v){
                $repost_id[] = $v->repost_id;
                $sta->updateOrCreate(['post_id'=>$v->repost_id], ['repost'=>$v->num]);
            }
            $sta->whereNotIn('post_id', $repost_id)->update(['repost'=>0]);

            $comment = DB::select("SELECT post_id, COUNT(1) AS num
                FROM post_comments
                WHERE STATUS=1
                GROUP BY post_id");
            foreach($comment as $v){
                $comment_post_id[] = $v->post_id;
                $sta->updateOrCreate(['post_id'=>$v->post_id], ['comment'=>$v->num]);
            }
            $sta->whereNotIn('post_id', $comment_post_id)->update(['comment'=>0]);

            $praise = DB::select("SELECT post_id, COUNT(1) AS num
                FROM post_praise
                WHERE STATUS=1
                GROUP BY post_id");
            foreach($praise as $v){
                $praise_post_id[] = $v->post_id;
                $sta->updateOrCreate(['post_id'=>$v->post_id], ['praise'=>$v->num]);
            }
            $sta->whereNotIn('post_id', $praise_post_id)->update(['praise'=>0]);
        }catch (\Exception $e){
            $this->failCron->title = 'post sta';
            $this->failCron->error_msg = $e->getMessage();
            $this->failCron->comment = 'post sta fail';
            $this->failCron->save();
        }
        return $this->sendSuccessResponse([]);
    }

    /**
     * post report statistic
     * @param PostSta $sta
     * @return \Illuminate\Http\JsonResponse
     */
    public function postReportSta(PostSta $sta){

        $report_post_id = [0];

        try{
            $report = DB::select("SELECT post_id, COUNT(1) AS num
                FROM post_reports
                WHERE STATUS=0
                GROUP BY post_id");
            foreach($report as $v){
                $report_post_id[] = $v->post_id;
                $sta->updateOrCreate(['post_id'=>$v->post_id], ['report'=>$v->num]);
            }
            $sta->whereNotIn('post_id', $report_post_id)->update(['report'=>0]);
        }catch (\Exception $e){
            $this->failCron->title = 'post report sta';
            $this->failCron->error_msg = $e->getMessage();
            $this->failCron->comment = 'post report sta fail';
            $this->failCron->save();
        }
        return $this->sendSuccessResponse([]);
    }

    /**
     * 检查VIP是否过期
     * @param User $user
     * @return \Illuminate\Http\JsonResponse
     */
    public function checkVIP(User $user){
        $time = time();
        $users = $user->where('vip_level', '>', 0)->where(function($where) use($time){
            $where->where('vip_validity', '<', $time)->orWhereNull('vip_validity');
        })->get();
        $user_ids = [];
        foreach($users as $v){
            try{
                $msgSystem = new MsgSystem();
                $msgSystem->to_id = $v->user_id;
                $msgSystem->message = Msg::VIP_expired();
                $msgSystem->type = 2;
                $msgSystem->save();
                $v->vip_level = 0;
                $v->vip_validity = 0;
                $v->save();
                $user_ids[] = $v->user_id;
            }catch (\Exception $e){
                $this->failCron->title = 'check VIP';
                $this->failCron->error_msg = $e->getMessage();
                $this->failCron->comment = 'check VIP fail user_id='.$v->user_id;
                $this->failCron->save();
                continue;
            }
        }
        if(!empty($user_ids)){
//            Easemob::singleton()->sendText(6, 'users', [$user_ids], Msg::VIP_expired(), ['user_id'=>6, 'type'=>2]);
            dispatch(new SendSystemMsgJob($user_ids, Msg::VIP_expired(), 2, ['user_id'=>6, 'type'=>2]));
        }
        return $this->sendSuccessResponse([]);
    }

    /**
     * 清掉一些被删除的用户的数据
     * @return \Illuminate\Http\JsonResponse
     */
    public function deleteDeadUsers(){
        DB::beginTransaction();
        try{
            $time = time();
            DB::update("UPDATE `posts`
                LEFT JOIN `users` ON `users`.`user_id` = `posts`.`user_id`
                SET `posts`.`status` = 0, `posts`.`updated_at` = {$time}
                WHERE users.user_id IS NULL AND `posts`.`status` = 1");
            DB::update("UPDATE `post_fav`
                LEFT JOIN `users` ON `users`.`user_id` = `post_fav`.`user_id`
                SET `post_fav`.`status` = 0, `post_fav`.`updated_at` = {$time}
                WHERE users.user_id IS NULL AND `post_fav`.`status` = 1");
            DB::update("UPDATE `post_praise`
                LEFT JOIN `users` ON `users`.`user_id` = `post_praise`.`user_id`
                SET `post_praise`.`status` = 0, `post_praise`.`updated_at` = {$time}
                WHERE users.user_id IS NULL AND `post_praise`.`status` = 1");
            DB::update("UPDATE `post_comments`
                LEFT JOIN `users` ON `users`.`user_id` = `post_comments`.`user_id`
                SET `post_comments`.`status` = 0, `post_comments`.`updated_at` = {$time}
                WHERE users.user_id IS NULL AND `post_comments`.`status` = 1");
            DB::update("UPDATE `shop_comments`
                LEFT JOIN `users` ON `users`.`user_id` = `shop_comments`.`user_id`
                SET `shop_comments`.`status` = 0, `shop_comments`.`updated_at` = {$time}
                WHERE users.user_id IS NULL AND `shop_comments`.`status` = 1");
            DB::update("UPDATE `follow`
                LEFT JOIN `users` ON `users`.`user_id` = `follow`.`user_id`
                SET `follow`.`status` = 0, `follow`.`updated_at` = {$time}
                WHERE users.user_id IS NULL AND `follow`.`status` = 1");
            DB::update("UPDATE `follow`
                LEFT JOIN `users` ON `users`.`user_id` = `follow`.`follow_id`
                SET `follow`.`status` = 0, `follow`.`updated_at` = {$time}
                WHERE users.user_id IS NULL AND `follow`.`status` = 1");
            DB::commit();
        }catch (\Exception $e){
            DB::rollBack();
            $this->failCron->title = 'delete dead user';
            $this->failCron->error_msg = $e->getMessage();
            $this->failCron->comment = 'delete dead user fail';
            $this->failCron->save();
            return $this->sendFailResponse(500, 50000);
        }
        return $this->sendSuccessResponse([]);
    }

    /**
     * 余额处理
     * @param Balance $balance
     * @param User $user
     * @return \Illuminate\Http\JsonResponse
     */
    public function balance(Balance $balance, User $user){
        DB::beginTransaction();
        try{
            $time = time()-86400*3;
            $author_balance = DB::select("SELECT bl.*
                FROM balance_log bl
                LEFT JOIN posts p ON bl.origin_id=p.id
                WHERE bl.`status`='".ConstVar::$BALANCE_STATUS_WAITING."'
                AND bl.origin_type='".ConstVar::$BALANCE_ORIGIN_POST."'
                AND p.income_flow='".ConstVar::$POST_INCOME_FLOW_AUTHOR."'
                AND bl.created_at <= $time LIMIT 1000");
            foreach($author_balance as $v){
                $author_balance_ids[] = $v->id;
                $user->where('user_id', $v->to_id)->increment('balance', $v->balance);
            }
            if(!empty($author_balance_ids)){
                $balance->whereIn('id', $author_balance_ids)->update(['status'=>1]);
            }

            $refund_balance = DB::select("SELECT bl.*
                FROM balance_log bl
                LEFT JOIN posts p ON bl.origin_id=p.id
                WHERE bl.`status`='".ConstVar::$BALANCE_STATUS_WAITING."'
                AND bl.origin_type='".ConstVar::$BALANCE_ORIGIN_POST."'
                AND p.income_flow='".ConstVar::$POST_INCOME_FLOW_REFUND."'
                AND bl.created_at <= $time LIMIT 1000");
            foreach($refund_balance as $v){
                $refund_balance_ids[] = $v->id;
                $user->where('user_id', $v->from_id)->increment('balance', $v->balance);
                $balance->add(//退款日志
                    Msg::post_refund().'_'.Msg::system_refund(),
                    0,//from_id
                    $v->from_id,
                    ConstVar::$BALANCE_CHANNEL_BALANCE,
                    ConstVar::$BALANCE_CHANNEL_BALANCE,
                    ConstVar::$ORDER_TYPE_POST,
                    $v->origin_id,
                    $v->balance,
                    $v->purchase_id,
                    $v->hong_bao_id,
                    $v->hong_bao_amount,
                    ConstVar::$BALANCE_STATUS_SUCCESS,
                    ConstVar::$BALANCE_TYPE_SYSTEM,
                    ConstVar::$BALANCE_TYPE_USER,
                    ConstVar::$BALANCE_REFUND
                );
            }
            if(!empty($refund_balance_ids)){
                $balance->whereIn('id', $refund_balance_ids)->update(['status'=>2]);
            }
            DB::commit();
        }catch (\Exception $e){
            DB::rollBack();
            $this->failCron->title = 'balance';
            $this->failCron->error_msg = $e->getMessage();
            $this->failCron->comment = 'balance fail';
            $this->failCron->save();
            return $this->sendFailResponse(500, 50000);
        }
        return $this->sendSuccessResponse([]);
    }

    public function shopHasHB(HongBaoTemplate $hongBaoTemplate, Shop $shop){
        try{
            $shop_ids = $hongBaoTemplate->select('shop_id')->where('status', 1)
                ->where('shop_id', '<>', 0)->get()->toArray();
            $shop->whereIn('shop_id', $shop_ids)->update(['hongbao'=>1]);
            $shop->whereNotIn('shop_id', $shop_ids)->update(['hongbao'=>0]);
        }catch (\Exception $e){
            $this->failCron->title = 'shopHasHB';
            $this->failCron->error_msg = $e->getMessage();
            $this->failCron->comment = 'shopHasHB fail';
            $this->failCron->save();
        }
    }

    public function cityStatistics(){
        try{
//            for($i=0;$i<=3;$i++) {
//                $today = Carbon::parse('-'.$i.' day')->startOfDay();
//                $yesterday = Carbon::parse('-'.($i+1).' day')->startOfDay();
            $today = Carbon::today();
            $yesterday = Carbon::yesterday();
            $post = District::selectRaw('districts.id, count(posts.id) as post')
                ->leftJoin('posts', function ($join) use ($today, $yesterday) {
                    $join->on('districts.id', 'posts.city_id')
                        ->whereBetween('posts.created_at', [$yesterday->timestamp, $today->timestamp]);
                })
                ->where('districts.type', 2)
                ->where('public', 1)
                ->groupBy('districts.id')->get();
            $user = District::selectRaw('districts.id, count(users.user_id) as user')
                ->leftJoin('users', function ($join) use ($today, $yesterday) {
                    $join->on('districts.id', 'users.mobile_city_id')
                        ->whereBetween('users.created_at', [$yesterday->timestamp, $today->timestamp]);
                })
                ->where('districts.type', 2)
                ->where('public', 1)
                ->groupBy('districts.id')->get();
            $post_comment = District::selectRaw('districts.id, count(post_comments.id) as post_comment')
                ->leftJoin('posts', function ($join) {
                    $join->on('districts.id', 'posts.city_id');
                })
                ->leftJoin('post_comments', function ($join) use ($today, $yesterday) {
                    $join->on('posts.id', 'post_comments.post_id')
                        ->whereBetween('post_comments.created_at', [$yesterday->timestamp, $today->timestamp]);
                })
                ->where('districts.type', 2)
                ->where('public', 1)
                ->groupBy('districts.id')->get();
            $shop_comment = District::selectRaw('districts.id, count(shop_comments.id) as shop_comment')
                ->leftJoin('shops', function ($join) {
                    $join->on('districts.id', 'shops.city_id');
                })
                ->leftJoin('shop_comments', function ($join) use ($today, $yesterday) {
                    $join->on('shops.shop_id', 'shop_comments.shop_id')
                        ->whereBetween('shop_comments.created_at', [$yesterday->timestamp, $today->timestamp]);
                })
                ->where('districts.type', 2)
                ->where('public', 1)
                ->groupBy('districts.id')->get();
            foreach ($post as $v) {
                CityStatistics::updateOrCreate(['city_id' => $v->id, 'day' => $yesterday->toDateString()], ['post' => $v->post]);
            }
            foreach ($user as $v) {
                CityStatistics::updateOrCreate(['city_id' => $v->id, 'day' => $yesterday->toDateString()], ['user' => $v->user]);
            }
            foreach ($post_comment as $v) {
                CityStatistics::updateOrCreate(['city_id' => $v->id, 'day' => $yesterday->toDateString()], ['post_comment' => $v->post_comment]);
            }
            foreach ($shop_comment as $v) {
                CityStatistics::updateOrCreate(['city_id' => $v->id, 'day' => $yesterday->toDateString()], ['shop_comment' => $v->shop_comment]);
            }
//            }
        }catch (\Exception $e){
            $this->failCron->title = 'cityStatistics';
            $this->failCron->error_msg = $e->getMessage();
            $this->failCron->comment = 'cityStatistics fail';
            $this->failCron->save();
        }
    }

    public function shopWithdrawBatch(ShopWithdrawApply $apply, Shop $shop, Balance $balance){
        $reject_applies = $apply->where('batch', 0)->where('status', 2)->get();
        foreach($reject_applies as $v){
            if(empty($v->comment)){
                $v->comment = Msg::contact_cs();
            }
            $shop->where('shop_id', $v->shop_id)->increment('balance', $v->apply_amount);
            dispatch(new SendSystemMsgJob([$v->user_id], Msg::shop_withdraw_reject($v->comment), 0));
            dispatch(new PushMsgJob([$v->user_id], Msg::shop_withdraw_reject($v->comment), 0));
            $v->batch = 1;
            $v->save();
        }
        $agree_applies = $apply->where('batch', 0)->where('status', 1)->get();
        foreach($agree_applies as $v){
            $balance->add(
                Msg::withdraw().'_'.Msg::withdraw(),
                $v->shop_id,
                $v->user_id,
                ConstVar::$BALANCE_CHANNEL_BALANCE,
                ConstVar::$BALANCE_CHANNEL_BANK,
                ConstVar::$ORDER_TYPE_WITHDRAW,
                0,
                $v->apply_amount,
                0,
                0,
                0,
                ConstVar::$BALANCE_STATUS_SUCCESS,
                ConstVar::$BALANCE_TYPE_SHOP,
                ConstVar::$BALANCE_TYPE_USER
            );
            dispatch(new SendSystemMsgJob([$v->user_id], Msg::shop_withdraw_agree(), 0));
            dispatch(new PushMsgJob([$v->user_id], Msg::shop_withdraw_agree(), 0));
            $v->batch = 1;
            $v->save();
        }
    }

    public function userWithdrawBatch(UserWithdrawApply $apply, User $user,
                                      MLMBalance $MLMBalance, Balance $balance,
                                      MLMBalanceLog $MLMBalanceLog){
        $reject_applies = $apply->where('batch', 0)->where('status', 2)->get();
        foreach($reject_applies as $v){
            if(empty($v->comment)){
                $v->comment = Msg::contact_cs();
            }
            if($v->type == 1){
                $user->where('user_id', $v->user_id)->increment('balance', $v->apply_amount);
            }else{
                $MLMBalance->where('user_id', $v->user_id)->increment('balance', $v->apply_amount);
            }
            dispatch(new SendSystemMsgJob([$v->user_id], Msg::user_withdraw_reject($v->comment), 0));
            $v->batch = 1;
            $v->save();
        }
        $agree_applies = $apply->where('batch', 0)->where('status', 1)->get();
        foreach($agree_applies as $v){
            $aop = new \AopClient();
            $aop->gatewayUrl = env('ALIPAY_GATEWAY');
            $aop->appId = env('ALIPAY_APPID');
            $aop->rsaPrivateKey = env('ALIPAY_RSA_PRIVATE_KEY');
            $aop->format = "json";
            $aop->charset = "UTF-8";
            $aop->signType = "RSA2";
            $aop->alipayrsaPublicKey = env('ALIPAY_RSA_PUBLISH_KEY');
            $ali_request = new \AlipayFundTransToaccountTransferRequest ();
            $ali_request->setBizContent("{" .
                "\"out_biz_no\":\"userwithdraw".$v->id."\"," .
                "\"payee_type\":\"ALIPAY_LOGONID\"," .
                "\"payee_account\":\"{$v->acc}\"," .
                "\"amount\":\"{$v->withdraw_amount}\"," .
                "\"payer_show_name\":\"夜城\"," .
                "\"payee_real_name\":\"{$v->acc_name}\"," .
                "\"remark\":\"提现\"" .
                "  }");
            $result = $aop->execute ($ali_request);
            $responseNode = str_replace(".", "_", $ali_request->getApiMethodName()) . "_response";
            $resultCode = $result->$responseNode->code;
            if(!empty($resultCode)&&$resultCode == 10000){
                $v->comment = '成功';
                $v->status = 4;
                if($v->type == 1){
                    $balance->add(
                        Msg::withdraw().'_'.Msg::withdraw(),
                        $v->user_id,
                        $v->user_id,
                        ConstVar::$BALANCE_CHANNEL_BALANCE,
                        ConstVar::$BALANCE_CHANNEL_ALIPAY,
                        ConstVar::$ORDER_TYPE_WITHDRAW,
                        0,
                        $v->apply_amount,
                        0,
                        0,
                        0,
                        ConstVar::$BALANCE_STATUS_SUCCESS,
                        ConstVar::$BALANCE_TYPE_USER,
                        ConstVar::$BALANCE_TYPE_USER
                    );
                }else{
                    $MLMBalanceLog->add($v->user_id, $v->user_id, $v->apply_amount, 3);
                }

                dispatch(new SendSystemMsgJob([$v->user_id], Msg::user_withdraw_agree(), 0));
            } else {
                $v->comment = '支付宝错误信息：'.$result->$responseNode->sub_msg;
                $v->status = 3;
                if($v->type == 1){
                    $user->where('user_id', $v->user_id)->increment('balance', $v->apply_amount);
                }else{
                    $MLMBalance->where('user_id', $v->user_id)->increment('balance', $v->apply_amount);
                }
                dispatch(new SendSystemMsgJob([$v->user_id], Msg::user_withdraw_reject($result->$responseNode->sub_msg), 0));
            }

            $v->batch = 1;
            $v->save();
        }
    }

    public function userPointBatch(UserPointLog $log, UserPoint $userPoint){
        DB::beginTransaction();
        try{
            $yesterday = Carbon::yesterday()->timestamp;
            $log->where('status', ConstVar::$POINT_STATUS_WAITING)
                ->where('created_at', '<', $yesterday)
                ->update(['status'=>ConstVar::$POINT_STATUS_AGREE]);

            $logs = $log->where('status', ConstVar::$POINT_STATUS_AGREE)->get();
            $ids = [];
            $user_ids = [];

            foreach($logs as $v){
                $ids[] = $v->id;
                $user_ids[] = $v->user_id;
            }
            $log->whereIn('id', $ids)->update(['status' => ConstVar::$POINT_STATUS_SUCCESS]);
            foreach($user_ids as $user_id){
                $points = $log->where('user_id', $user_id)
                    ->where('status', ConstVar::$POINT_STATUS_SUCCESS)
                    ->sum('point');
                $userPoint->updateOrCreate(['user_id'=>$user_id], ['point'=>$points, 'total_point'=>$points]);
            }
            DB::commit();
        }catch (\Exception $e){
            DB::rollBack();
            $this->failCron->title = 'userPointBatch';
            $this->failCron->error_msg = $e->getMessage();
            $this->failCron->comment = 'userPointBatch fail';
            $this->failCron->save();
        }
    }

    public function checkTransferPayment(){
        //todo
    }
}